#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

#include "widget.h"
#include "callback.h"

// (private) size of the hash table
static size_t cbsize=0;
// (private) hash table contains hwnd - widget binding
static callback_t **cbmap=NULL;

void callback_initialize(size_t table_size)
{
    size_t i;
    // allocate memory for the hash table
    cbsize = table_size;
    assert(cbsize > 0);
    cbmap = malloc(sizeof(callback_t *) * cbsize);
    assert(cbmap != NULL);
    // create dummy nodes
    for(i=0; i<cbsize; i++)
    {
        callback_t *dummy = malloc(sizeof(callback_t));
        assert(dummy != NULL);
        dummy->hwnd = 0;
        dummy->widget = NULL;
        dummy->next = dummy; // point at it self
        cbmap[i] = dummy;
    }
}

callback_t *callback_bind(HWND hwnd, widget_t *widget)
{
    size_t hash = (uint32_t)hwnd % cbsize;
    // assert that the hwnd is not already binded
    callback_t *itr = cbmap[hash];
    while(itr->next != itr) {
        assert(itr->hwnd != hwnd);
        itr = itr->next;
    }
    // create a new node and set its value
    callback_t *node = malloc(sizeof(callback_t));
    node->hwnd = hwnd;
    node->widget = widget;
    node->next = cbmap[hash];
    // set the default callback functions
    node->free = callback_free;
    node->close = callback_close;
    node->paint = callback_paint;
    node->mouse_move = callback_mouse_move;
    node->lbutton_down = callback_lbutton_down;
    node->lbutton_up = callback_lbutton_up;
    node->rbutton_down = callback_rbutton_down;
    node->rbutton_up = callback_rbutton_up;
    node->key_down = callback_key_down;
    node->key_up = callback_key_up;
    node->mouse_enter = callback_mouse_enter;
    node->mouse_leave = callback_mouse_leave;
    // add the node to the table
    cbmap[hash]= node;
    // return a pointer to the callback node
    return node;
}

callback_t *callback_find(HWND hwnd)
{
    const size_t hash = (uint32_t)hwnd % cbsize;
    // search for the hwnd in the hash table
    callback_t *itr = cbmap[hash];
    while(itr != itr->next) {
        if(hwnd == itr->hwnd) {
            return itr;
        }
        itr = itr->next;
    }
    // should never go to here!
    fprintf(stderr, "callback_find_node error: hwnd not binded!\n");
    exit(-1);
}

void callback_finalize()
{
    size_t i;

    for(i=0; i<cbsize; i++)
    {
        callback_t *itr = cbmap[i];
        while(itr != itr->next) {
            callback_t *tmp = itr;
            // let the widget free it self
            assert(tmp->widget != NULL);
            tmp->free(tmp->widget);
            // free the callback node
            free(tmp);
            itr = itr->next;
        }
        // remove the dummy node
        free(itr);
    }

    // free the hash table
    free(cbmap);
}

void callback_free(widget_t *self)
{
    free(self);
}

BOOL callback_close(widget_t *self)
{
    return TRUE;
}

void callback_paint(widget_t *self, HDC hdc)
{
}

void callback_mouse_move(self,x,y,vkey)
    widget_t *self;
    int32_t x,y;
    uint32_t vkey;
{
}

void callback_lbutton_down(self,x,y,vkey)
    widget_t *self;
    int32_t x,y;
    uint32_t vkey;
{
}

void callback_lbutton_up(self,x,y,vkey)
    widget_t *self;
    int32_t x,y;
    uint32_t vkey;
{
}

void callback_rbutton_down(self,x,y,vkey)
    widget_t *self;
    int32_t x,y;
    uint32_t vkey;
{
}

void callback_rbutton_up(self,x,y,vkey)
    widget_t *self;
    int32_t x,y;
    uint32_t vkey;
{
}

void callback_key_down(widget_t *self, uint32_t vkey)
{
}

void callback_key_up(widget_t *self, uint32_t vkey)
{
}

void callback_mouse_enter(self,x,y,vkey)
    widget_t *self;
    int32_t x,y;
    uint32_t vkey;
{
}

void callback_mouse_leave(widget_t *self)
{
}

